package com.example.config.mybatis;

import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.binding.MapperMethod.ParamMap;

import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.Properties;

/**
 * mybatis拦截器，自动注入创建人、创建时间、修改人、修改时间
 * @Author scott
 * @Date  2019-01-19
 *
 */
//这种写法的mybatis拦截器不需要手动注册，SpringBoot会自动注册
@Component
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class MybatisInterceptor implements Interceptor {

	/**
	 * 代理对象每次调用的方法，就是要进行拦截的时候要执行的方法。在这个方法里面做我们自定义的逻辑处理
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Object[] args = invocation.getArgs();
		MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];//第一个参数是MappedStated
		Object parameter = invocation.getArgs()[1];//第二个 参数是传参数的类型

		//如果没有参数，直接返回
		if (parameter == null) {
			return invocation.proceed();
		}
		//执行语句的类型
		SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
		if(SqlCommandType.INSERT ==sqlCommandType){
			Field[] fields = parameter.getClass().getDeclaredFields();//根据class获取所有的属性
			for(Field field:fields){
				String name = field.getName();
				if(name.equals("createBy")){
					field.setAccessible(true);//破坏 create_by 的封装
					field.set(parameter,"zhangfei");
					field.setAccessible(false);
				}else if(name.equals("createTime")){
					field.setAccessible(true);//破坏 create_by 的封装
					field.set(parameter,new Date());
					field.setAccessible(false);
				}
			}
		}else if(SqlCommandType.UPDATE == sqlCommandType){
			Field[] fields = null;
			//最重要就是判断mybatis将参数包装成什么类型，实体或者map
			if (parameter instanceof ParamMap) {
				ParamMap<?> p = (ParamMap<?>) parameter;
				//mybatis给参数起的别名是et和param1，一般情况下，这两个都有。
				if (p.containsKey("et")) {
					parameter = p.get("et");
				} else {
					parameter = p.get("param1");
				}
				if (parameter == null) {
					//TODO 如果不传参数，看parameter是什么
					return invocation.proceed();
				}
				fields =  parameter.getClass().getDeclaredFields();//根据class获取所有的属性
			} else {
				fields =  parameter.getClass().getDeclaredFields();//根据class获取所有的属性
			}
			for(Field field:fields){
				String name = field.getName();
				if(name.equals("updateBy")){
					field.setAccessible(true);//破坏 create_by 的封装
					field.set(parameter,"admin");
					field.setAccessible(false);
				}else if(name.equals("updateTime")){
					field.setAccessible(true);//破坏 create_by 的封装
					field.set(parameter,new Date());
					field.setAccessible(false);
				}
			}
		}
		return invocation.proceed();
	}

	/**
	 * plugin方法是拦截器用于封装目标对象的，通过该方法我们可以返回目标对象本身，也可以返回一个它的代理
	 *
	 * 当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法 -- Plugin.wrap(target, this)
	 * 当返回的是当前对象的时候 就不会调用intercept方法，相当于当前拦截器无效
	 */
	@Override
	public Object plugin(Object target) {
		//返回代理类,用来为目标对象创建代理对象的方法，通常不需要覆写。
		return Plugin.wrap(target,this);
	}

	/**
	 * 用于在Mybatis配置文件中指定一些属性的，注册当前拦截器的时候可以设置一些属性
	 */
	@Override
	public void setProperties(Properties properties) {

	}
}
